/* Includes {{{ */
#if HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#if HAVE_STDLIB_H
#include <stdlib.h>
#endif /* HAVE_STDLIB_H */

#if HAVE_STDIO_H
#include <stdio.h> 
#endif /* HAVE_STDIO_H */

#if HAVE_SIGNAL_H
#include <signal.h>		/* sig_atomic_t */
#endif /* HAVE_SIGNAL_H */

#if HAVE_STDARG_H
#include <stdarg.h>     /* ANSI C header file */
#endif /* HAVE_STDARG_H */

#if HAVE_ERRNO_H
#include <errno.h>
#endif /* HAVE_ERRNO_H */

#if HAVE_STRING_H
#include <string.h>
#endif /* HAVE_STRING_H */

#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#if HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif

#define MAXLINE 4096

#ifdef __cplusplus
extern "C"
{
#endif

#include "tgdb.h"
#include "tgdb_command.h"
#include "logger.h"

#ifdef __cplusplus
}
#endif

#include "ITarget.h"
#include "ICustomeRequest.h"

#ifndef CTGDB_H_
#define CTGDB_H_
/**
 * The TGDB context data structure.
 */
class Ctgdb
{
	private:
		/** A client context to abstract the debugger.  */
		struct tgdb_client_context *tcc;

		int num_loggers;

		bool isInitiolized;
		/** Reading from this will read from the debugger's output */
		int debugger_stdout;

		/** Writing to this will write to the debugger's stdin */
		int debugger_stdin;

		/** Reading from this will read the stdout from the program being debugged */
		int inferior_stdout;

		/** Writing to this will write to the stdin of the program being debugged */
		int inferior_stdin;

		struct logger* m_logger;
		/***************************************************************************
		 * All the queue's the clients can run commands through
		 * The different queue's can be slightly confusing.
		 **************************************************************************/

		/**
		 * The commands that need to be run through GDB.
		 *
		 * This is a buffered queue that represents all of the commands that TGDB
		 * needs to execute. These commands will be executed one at a time. That is,
		 * 1 command will be issued, and TGDB will wait for the entire response before
		 * issuing any more commands. It is even possible that while this command is
		 * executing, that the client context will add commands to the 
		 * oob_command_queue. If this happens TGDB will execute all of the commands
		 * in the oob_command_queue before executing the next command in this queue. 
		 */
		struct queue *gdb_input_queue;

		/** 
		 * The commands that the client has requested to run.
		 *
		 * TGDB buffers all of the commands that the FE wants to run. That is,
		 * if the FE is sending commands faster than TGDB can pass the command to
		 * GDB and get a response, then the commands are buffered until it is there
		 * turn to run. These commands are run in the order that they are recieved.
		 *
		 * This is here as a convience to the FE. TGDB currently does not access 
		 * these commands. It provides the push/pop functionality and it erases the
		 * queue when a control_c is received.
		 */
		struct queue *gdb_client_request_queue;

		/** 
		 * The out of band input queue. 
		 *
		 * These commands are used by the client context. When a single client context 
		 * command is run, sometimes it will discover that it needs to run other commands
		 * in order to satisfy the functionality requested by the GUI. For instance, a 
		 * single GUI function request could take N client context commands to generate
		 * a response. Also, to make things worse, sometimes the client context doesn't 
		 * know if it will take 1 or N commands to satisfy the request until after it has 
		 * sent and recieved the information from the first command. Thus, while the 
		 * client context is processing the first command, it may add more commands to 
		 * this queue to specify that these commands need to be run before any other 
		 * commands are sent to GDB.
		 *
		 * These commands should *always* be run first.
		 */
		struct queue *oob_input_queue;

		/** These are 2 very important state variables.  */

		/**
		 * If set to 1, libtgdb thinks the lower level subsystem is capable of 
		 * recieving another command. It needs this so that it doesn't send 2
		 * commands to the lower level before it can say it can't recieve a command.
		 * At some point, maybe this can be removed?
		 * When its set to 0, libtgdb thinks it can not send the lower level another
		 * command.  */
		int IS_SUBSYSTEM_READY_FOR_NEXT_COMMAND;

		/** If ^c was hit by user */
		sig_atomic_t control_c;

		/**
		 * This is the last GUI command that has been run.
		 * It is used to display to the client the GUI commands.
		 *
		 * It will either be NULL when it is not set or it should be
		 * the last GUI command run. If it is non-NULL it should be from the heap.
		 * As anyone is allowed to call free on it.  */
		char *last_gui_command;

		/**
		 * This is a TGDB option.
		 * It determines if the user wants to see the commands the GUI is running.
		 * 
		 * If it is 0, the user does not want to see the commands the GUI is 
		 * running. Otherwise, if it is 1, it does.  */
		int show_gui_commands;

		/**
		 * This is the queue of commands TGDB has currently made to give to the 
		 * front end.  */
		struct tgdb_list *command_list;

		/** An iterator into command_list. */
		tgdb_list_iterator *command_list_iterator;

		/**
		 * When GDB dies (purposely or not), the SIGCHLD is sent to the application controlling TGDB.
		 * This data structure represents the fact that SIGCHLD has been sent.
		 *
		 * This currently does not need to track if more than 1 SIGCHLD has been received. So
		 * no matter how many are receieved, this will only be 1. Otherwise if none have been
		 * received this will be 0.  */
		int has_sigchld_recv;
		ITarget* m_target;
	public:

		Ctgdb();
		/**
		 * This initializes a tgdb library instance. It starts up the debugger and 
		 * returns all file descriptors the client must select on.
		 *
		 * The client must call this function before any other function in the 
		 * tgdb library.
		 *
		 * \param debugger
		 * The path to the desired debugger to use. If this is NULL, then just
		 * "gdb" is used.
		 *
		 * \param argc
		 * The number of arguments to pass to the debugger
		 *
		 * \param argv
		 * The arguments to pass to the debugger    
		 *
		 * \param debugger_fd
		 * The descriptor to the debugger's I/O
		 *
		 * \param inferior_fd
		 * The descriptor to the I/O of the program being debugged.
		 *
		 * @return
		 * NULL if OK
		 */
		int Initialize (const char *debugger,
				int argc, char **argv,
				int *debugger_fd, int *inferior_fd);

		bool IsInitiolized();



		int Logger_write_pos( const char *file,  int line, const char *fmt, ...);

		/**
		 * This will terminate a libtgdb session. No functions should be called on
		 * the tgdb context passed into this function after this call.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * @return
		 * 0 on success or -1 on error
		 */
		int Shutdown ();

		/* Status Commands*/
		/******************************************************************************/
		/**
		 * @name Status Commands
		 * These functions are for querying the tgdb context.
		 */
		/******************************************************************************/


		/**
		 * If a function returns an error, this can be called to report more
		 * about the type of error, or the error message itself.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * @return
		 * Error Message or NULL on error
		 */
		char* GetErrorMsg();

		/**
		 * This will check to see if TGDB is currently capable of receiving another command.
		 * 
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \param is_busy
		 * Will return as 1 if tgdb is busy, otherwise 0.
		 *
		 * \return
		 * 0 on success, or -1 on error
		 */
		int IsBusy (int *is_busy);


		/* Input/Output commands */
		/******************************************************************************/
		/**
		 * @name Input/Output commands
		 * These functions are for communicating I/O with the tgdb context.
		 */
		/******************************************************************************/


		/**
		 * Have TGDB process a command.
		 * 
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 * 
		 * \param request
		 * The requested command to have TGDB process.
		 *
		 * \return
		 * 0 on success or -1 on error
		 */
		int ProcessCommand(tgdb_request_ptr request, char* param);

		int ProcessCommand(tgdb_request_ptr request);

		/**
		 * This function does most of the dirty work in TGDB. It is capable of 
		 * processing the output of the debugger, to either satisfy a previously 
		 * made request, or to simply get console output for the caller to have.
		 *
		 * The data returned from this function is the console output of the 
		 * debugger.
		 * 
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \param buf
		 * The output of the debugger will be returned in this buffer.
		 * The buffer passed back will not exceed N in size.
		 *
		 * \param n
		 * Tells libtgdb how large the buffer BUF is that the client passed in.
		 *
		 * \param is_finished
		 * If this is passed in as NULL, it is not set.
		 *
		 * If it is non-null, it will be set to 1 if TGDB finished processing the
		 * current request. Otherwise, it will be set to 0 if TGDB needs more input
		 * in order to finish processing the current requested command.
		 *
		 * @return
		 * The number of valid bytes in BUF on success, or -1 on error.
		 */
		//size_t Process(char *buf, size_t n, int *is_finished, enum tgdb_respond_type* target);

		size_t Process(char *buf, size_t n, int *is_finished, ITarget** target);
		/**
		 * This sends a byte of data to the program being debugged.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \param c
		 * The character to pass to the program being debugged.
		 *
		 * @return
		 * 0 on success or -1 on error
		 */
		int Send_inferior_char(char c);

		/**
		 * Gets the ouput from the program being debugged.
		 * 
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \param buf
		 * The output of the program being debugged will be returned here.
		 *
		 * \param n
		 * Tells libtgdb how large the buffer BUF is.
		 *
		 * @return
		 * The number of valid bytes in BUF on success, or 0 on error.
		 */
		size_t Recv_inferior_data (char *buf, size_t n);


		/* Getting Data out of TGDB */
		/******************************************************************************/
		/**
		 * @name Getting Data out of TGDB
		 * These functions are for dealing with getting back data from TGDB
		 */
		/******************************************************************************/

		/**
		 * Gets a response from TGDB.
		 * This should be called after tgdb_recv_debugger_data
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * @return
		 * A valid response if responses still exist.
		 * Null if no more responses exist.
		 */
		struct tgdb_response *Get_response();
		//enum tgdb_respond_type Get_target();
		ITarget* Get_target();
		/**
		 * This will traverse all of the responses that the context tgdb currently
		 * has and will print them. It is currently used for debugging purposes.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 */
		void Traverse_responses();

		/**
		 * This will free all of the memory used by the responses that tgdb returns.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 */
		void Delete_responses();


		/* Inferior tty commands */
		/******************************************************************************/
		/**
		 * @name Inferior tty commands
		 * These functinos are used to alter the tty state of the inferior program.
		 * These functions currently are not always supported. If the annotate_two
		 * subsytem is being used, the tty commands are supported.
		 */
		/******************************************************************************/

		/**
		 * This allocates a new tty and tells the debugger to use it for I/O
		 * with the program being debugged.
		 *
		 * Whatever was left in the old tty is lost, the debugged program starts
		 * with a new tty.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * NOTE
		 *  The return value only indicates whether the tty was allocated properly,
		 *  not whether the debugger accepted the tty, since this can only be determined 
		 *  when the debugger responds, not when the command is given.
		 *
		 * @return
		 * 0 on success or -1 on error
		 */
		int Tty_new();

		/**
		 * Gets the name of file that debugger is using for I/O with the program
		 * being debugged.
		 * 
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * @return
		 * Name of tty or NULL on error.
		 */
		const char * Get_tty_name();

		/* Functional commands */
		/******************************************************************************/
		/**
		 * @name Functional commands
		 * These functinos are used to ask the TGDB context to perform a task.
		 */
		/******************************************************************************/

		/**
		 * This sends a console command to the debugger (GDB).
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \param command
		 * The null terminated command to pass to GDB as a console command.
		 * 
		 * \return
		 * Will return as a tgdb request command on success, otherwise NULL.
		 */
		tgdb_request_ptr Request_run_console_command(const char *command);

		/**
		 * Gets a list of source files that make up the program being debugged.
		 *
		 * This function does not actually do anything but put a command in the 
		 * queue to be run when libtgdb is ready. When the libtgdb runs the
		 * command to get the inferior's source files, it will return 1 of 2
		 * things next time tgdb_recv is called.
		 *
		 * If the function succeeds the gui will get back TGDB_UPDATE_SOURCE_FILES
		 * containing a list of all the source files. Otherwise the gui will get
		 * back TGDB_SOURCES_DENIED.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \return
		 * Will return as a tgdb request command on success, otherwise NULL.
		 */
		tgdb_request_ptr Request_inferiors_source_files();

		/**
		 * This gets the absolute path and the relative path from the given file, 
		 * FILE. The file parameter should have been returned by 
		 * tgdb_request_inferiors_source_files. It can return both relative and 
		 * absolute filenames.
		 *
		 * Basically, if the parameter file is a relative path OR an absolute path, 
		 * both the relative path and the absolute path will be returned in the 
		 * response.
		 *
		 * If this functions succeeds TGDB_ABSOLUTE_SOURCE_ACCEPTED will be returned.
		 * Otherwise, TGDB_ABSOLUTE_SOURCE_DENIED gets returned.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \param file
		 * The relative filename to get the absolute path of. If this is passed in as
		 * NULL, then this function will return NULL.
		 *
		 * \return
		 * Will return as a tgdb request command on success, otherwise NULL.
		 */
		tgdb_request_ptr Request_filename_pair(const char *file);

		/**
		 * This will ask the debugger for it's current file and line number.
		 * It will return to the caller a tgdb_response with the 
		 * response->update_source_files set. This is the same response you 
		 * will get when TGDB asynchronously sends the update_file_postition.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 * 
		 * \param on_starup
		 * This variable can be set to 1 if the front end wants to probe GDB
		 * for the initial file and location of the program being debugged.
		 * However, each initial time after that, this variable should be 
		 * set to 0.
		 *
		 * \return
		 * Will return as a tgdb request command on success, otherwise NULL.
		 */
		tgdb_request_ptr Request_current_location(int on_startup);

		/**
		 * This tells libtgdb to run a command through the debugger.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \param c
		 * This is the command that libtgdb should run through the debugger.
		 *
		 * @return
		 * Will return as a tgdb request command on success, otherwise NULL.
		 */
		tgdb_request_ptr Request_run_debugger_command(enum tgdb_command_type c);

		/**
		 * Modify's a breakpoint.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \param file
		 * The file to set the breakpoint in.
		 *
		 * \param line
		 * The line in FILE to set the breakpoint in.
		 *
		 * \param b
		 * Determines what the user wants to do with the breakpoint.
		 *
		 * @return
		 * Will return as a tgdb request command on success, otherwise NULL.
		 */
		tgdb_request_ptr Request_modify_breakpoint(const char *file,
				int line, 
				enum tgdb_breakpoint_action b);

		/**
		 * Used to get all of the possible tab completion options for LINE.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \param line
		 * The line to tab complete.
		 *
		 * \return
		 * Will return as a tgdb request command on success, otherwise NULL.
		 */
		tgdb_request_ptr Request_complete(const char *line);


		/* TGDB Queue commands {{{*/
		/******************************************************************************/
		/**
		 * @name Queuing of TGDB commands
		 * These functinos are used to allow clients to queue multiple commands for 
		 * TGDB to be able to run.
		 */
		/******************************************************************************/

		/**
		 * Have TGDB append a command to it's list of commands it needs to run.
		 * 
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 * 
		 * \param request
		 * The requested command to have TGDB process.
		 *
		 * \return
		 * 0 on success or -1 on error
		 */
		int Queue_append(tgdb_request_ptr request);

		/**
		 * Get a tgdb_request command back from TGDB.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \return
		 * The requested command at the top of the stack, or NULL on error.
		 * Popping a command when the queue is empty is also considered an error,
		 * and NULL will be returned.
		 */
		tgdb_request_ptr Queue_pop();

		/**
		 * Get's the number of items that are in the queue for TGDB to run.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \size
		 * The number of items in the list.
		 *
		 * \return
		 * 0 on success or -1 on error
		 */
		int Queue_size (int *size);


		/* Signal Handling Support */
		/******************************************************************************/
		/**
		 * @name Signal Handling Support
		 * These functinos are used to notify TGDB of signal recieved.
		 */
		/******************************************************************************/


		/**
		 * The front end can use this function to notify libtgdb that an
		 * asynchronous event has occurred. If signal SIGNUM is relavant
		 * to libtgdb, the appropriate processing will be done.
		 * Currently, TGDB only handles SIGINT,SIGTERM and SIGQUIT.
		 *
		 * libtgdb will remove all elements from it's queue when a SIGINT
		 * is received.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \param signum
		 * The signal number SIGNUM that has occured.
		 *
		 * @return
		 * 0 on success or -1 on error
		 */
		int Signal_notification (int signum);

		/* Config Options */
		/******************************************************************************/
		/**
		 * @name TGDB Config Options
		 * These functinos are used to change the state TGDB works. TGDB can currently
		 * only be configured through this interface.
		 */
		/******************************************************************************/


		/**
		 * This sets the verbosity of the GUI's commands.
		 * If the value is set to 0, the GUI's commands will not be shown.
		 * If the value is set to 1, the GUI's commands will be shown.
		 * The default value for TGDB is to be off.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \param value
		 * 0 to not show GUI commands, 1 to show them, otherwise nothing is done.
		 * You would use a value other than 0 or 1 just to query if the option is set.
		 *
		 * @return
		 * 1 if option is set, otherwise 0
		 */
		int Set_verbose_gui_command_output (int value);

		/**
		 * This will make TGDB handle error's in a verbose mode.
		 * The basically mean's that when TGDB find's an error, the message is
		 * printed to stdout/stderr. Normally this is not acceptable because TGDB can
		 * simple not print when any front end is using it. Imparticular, TGDB can
		 * not print when a curses based front end is using it.
		 *
		 * \param tgdb
		 * An instance of the tgdb library to operate on.
		 *
		 * \param value
		 * if -1, query to see if the option is set.
		 * If 0, the option will be turned off.
		 * If 1, the option will be turned on.
		 *
		 * @return
		 * 1 if option is set, otherwise 0
		 */
		int Set_verbose_error_handling (int value);
	private:
		int Initialize_config_dir (char *config_dir);
		int Initialize_logger_interface(char *config_dir);
		char * Get_client_command(enum tgdb_command_type c);
		char * Client_modify_breakpoint_call (const char *file, int line, enum tgdb_breakpoint_action b);
		bool Can_issue_command();
		bool Has_command_to_run();
		int Process_console_command(tgdb_request_ptr request);
		int Process_client_commands();
		int Process_info_sources(tgdb_request_ptr request);
		int Process_filename_pair (tgdb_request_ptr request);
		int Process_current_location(tgdb_request_ptr request);
		int ProcessCustomeRequest(ICustomeRequest* request);
		//int Process_debugger_command(tgdb_request_ptr request, char* param);
		void Handle_signals();
		int Process_debugger_command(tgdb_request_ptr request);
		int Process_modify_breakpoint (tgdb_request_ptr request);
		int Process_complete(tgdb_request_ptr request);
		//int Send(char *command, enum tgdb_command_choice command_choice, enum tgdb_respond_type target);
		int Send(char *command, enum tgdb_command_choice command_choice, ITarget* target);
		int Send(char *command, enum tgdb_command_choice command_choice);
		int Run_or_queue_command (struct tgdb_command *command);
		int Deliver_command(struct tgdb_command *command);
		int Unqueue_and_deliver_command();
		int Add_quit_command();
		int Get_quit_command (int *tgdb_will_quit);

};
#endif //CTGDB_H_
